﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
using Volo.Abp.Identity;
using Volo.Abp.Uow;
using static System.Formats.Asn1.AsnWriter;
using static Volo.Abp.Identity.Settings.IdentitySettingNames;

namespace GMall.AdministrationService.Menus
{
    public class MenuManager : DomainService
    {
        private readonly IRepository<Menu, Guid> _menuRepository;
        public MenuManager(IRepository<Menu, Guid> menuRepository)
        {
            _menuRepository = menuRepository;
        }
        public virtual async Task CreateAsync(Menu menu)
        {
            menu.SetCode(await GetNextChildCodeAsync(menu.ParentId));
            await ValidateMenuAsync(menu);
            await _menuRepository.InsertAsync(menu);
        }

        public virtual async Task DeleteAsync(Guid id)
        {
            var children = await FindChildrenAsync(id, true);
            foreach (var child in children)
            {
                await _menuRepository.DeleteAsync(child);
            }
            await _menuRepository.DeleteAsync(id);
        }

        public virtual async Task MoveAsync(Guid id, Guid? parentId)
        {
            var menu = await _menuRepository.GetAsync(id);
            if (menu.ParentId == parentId)
            {
                return;
            }
            var children = await FindChildrenAsync(id, true);
            var oldCode = menu.Code;
            var newCode = await GetNextChildCodeAsync(parentId);
            menu.SetCode(newCode);
            menu.SetParentId(parentId);
            await ValidateMenuAsync(menu);
            foreach (var child in children)
            {
                var childNewCode = Menu.AppendCode(menu.Code, Menu.GetRelativeCode(child.Code, oldCode));
                child.SetCode(childNewCode);
                await _menuRepository.UpdateAsync(child);
            }
            await _menuRepository.UpdateAsync(menu);
        }

        public virtual async Task<Menu> GetByIdAsync(Guid id)
        {
            var menu = await _menuRepository.FindAsync(m => m.Id == id);
            if (menu == null)
            {
                throw new EntityNotFoundException(typeof(Menu), id);
            }
            return menu;
        }
        public virtual async Task<Menu> GetByCodeAsync(string code)
        {
            var menu = await _menuRepository.FindAsync(m => m.Code == code);
            if (menu == null)
            {
                throw new EntityNotFoundException(typeof(Menu), code);
            }
            return menu;
        }
        #region 
        public async Task<List<Menu>> FindChildrenAsync(Guid? parentId, bool recursive = false)
        {
            if (!recursive)
            {
                return await _menuRepository.GetListAsync(o => o.ParentId == parentId, true);
            }
            if (!parentId.HasValue)
            {
                return await _menuRepository.GetListAsync(true);
            }
            string code = await GetCodeOrDefaultAsync(parentId.Value);
            return await _menuRepository.GetListAsync(m => m.Code.StartsWith(code) && m.Id != parentId.Value);
        }
        public virtual async Task<string> GetNextChildCodeAsync(Guid? parentId)
        {
            var lastChild = await GetLastChildOrNullAsync(parentId);
            if (lastChild != null)
            {
                return Menu.CalculateNextCode(lastChild.Code);
            }

            var parentCode = (parentId != null)
                ? await GetCodeOrDefaultAsync(parentId.Value)
                : null;

            return Menu.AppendCode(parentCode, Menu.CreateCode(1));
        }
        public virtual async Task<Menu> GetLastChildOrNullAsync(Guid? parentId)
        {
            var children = await GetChildrenAsync(parentId);
            return children.OrderBy(c => c.Code).LastOrDefault();
        }
        public virtual async Task<List<Menu>> GetChildrenAsync(Guid? parentId)
        {
            return await _menuRepository.GetListAsync(m => m.ParentId == parentId);
        }
        public virtual async Task<string> GetCodeOrDefaultAsync(Guid id)
        {
            var m = await _menuRepository.GetAsync(id);
            return m?.Code;
        }
        protected virtual async Task ValidateMenuAsync(Menu menu)
        {
            var siblings = (await FindChildrenAsync(menu.ParentId))
                .Where(m => m.Id != menu.Id)
                .ToList();

            if (siblings.Any(ou => ou.Name == menu.Name))
            {
                throw new ApplicationException($"{menu.Name}已存在");
            }
        }
        #endregion
    }
}
